package spider

import (
	"bufio"
	"gitee.com/yysf-xin/dproxy/pkg/model"
	"io"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"
)

type Proxy struct {
	model.Proxy

	Type      uint8
}

func (p *Proxy) TableName() string {
	return "proxy"
}

type ProxyCheck struct {
	IpDat   *QQwry
	Clients *sync.Pool
	limit   chan struct{}
}

type Checker interface {
	Check(cli *http.Client, proxy *url.URL) (*http.Response, error)
}

type HeadCheck string

func (h HeadCheck) Check(cli *http.Client, _ *url.URL) (*http.Response, error) {
	return cli.Head(string(h))
}

type BodyCheckFn func(resp *http.Response, proxy *url.URL) error

type BodyCheck struct {
	Url string
	Fn  BodyCheckFn
}

func BodyAsIp(resp *http.Response, proxy *url.URL) string {
	buf := make([]byte, 16)
	n, _ := io.ReadFull(resp.Body, buf)
	if n != 0 {
		host := string(buf[:n])
		if !strings.HasPrefix(proxy.Host, host) {
			return host
		}
	}
	return ""
}

func (h *BodyCheck) Check(cli *http.Client, proxy *url.URL) (resp *http.Response, err error) {
	resp, err = cli.Get(h.Url)
	if err == nil && h.Fn != nil {
		err = h.Fn(resp, proxy)
	}
	return
}

func NewCheck(ipDat string, size int) (c *ProxyCheck, err error) {
	var wry *QQwry
	if ipDat != "" {
		wry, err = NewQQwry(ipDat)
	}
	if err == nil {
		c = &ProxyCheck{
			IpDat: wry,
			Clients: &sync.Pool{New: func() interface{} {
				return &http.Client{
					Transport: &http.Transport{
						DisableKeepAlives: true,
					},
					Timeout: 15 * time.Second,
				}
			}},
			limit: make(chan struct{}, size),
		}
	}
	//path := "E:\\OneDriver\\OneDrive\\工作\\tools\\ip\\qqwry.dat"
	return
}

// "https://www.google.com"

func (c *ProxyCheck) CheckFile(path, tp string, checker Checker) error {
	// "testdata/live.txt"
	f, err := os.Open(path)
	if err != nil {
		return err
	}
	defer f.Close()

	gr := &sync.WaitGroup{}
	r := bufio.NewReader(f)
	sc := bufio.NewScanner(r)
	for sc.Scan() {
		addr := sc.Text()

		if strings.HasPrefix(addr, "#") {
			continue
		}

		if !strings.Contains(addr, "//") {
			addr = tp + addr
		}
		var u *url.URL
		u, err = url.Parse(addr)
		if err != nil {
			continue
		}
		gr.Add(1)

		c.limit <- struct{}{}

		if cli, ok := c.Clients.Get().(*http.Client); ok {
			cli.Transport.(*http.Transport).Proxy = http.ProxyURL(u)
			go func() {
				defer c.Clients.Put(cli)
				defer gr.Done()
				bT := time.Now()
				var resp *http.Response
				resp, err = checker.Check(cli, u)
				if resp != nil {
					resp.Body.Close()
				}
				span := int64(time.Since(bT)) / 1000 / 1000
				if err == nil {
					cu, ci := c.IpDat.Find(u.Host)
					println(u.Host, span, cu, ci)
				}
				<-c.limit
			}()
		} else {
			gr.Done()
		}
	}
	gr.Wait()
	return nil
}
